package org.springframework.ioc.beanfactory.beanprocess;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.BeanMetadataAttribute;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.ReflectionUtils;

public class CustomMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor {

	private BeanFactory beanFactory;
	
	public CustomMergedBeanDefinitionPostProcessor(BeanFactory beanFactory){
		this.beanFactory = beanFactory;
	}
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println(getClass()+" postProcessBeforeInitialization is running");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println(getClass()+" postProcessAfterInitialization is running");
		return bean;
	}

	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
		System.out.println(getClass()+" postProcessMergedBeanDefinition is running");
		Field[] fields = beanType.getDeclaredFields();
		List<ResourceNameKey> keys = new ArrayList();
		for(Field field : fields){
			ResourceName annotation = field.getAnnotation(ResourceName.class);
			if(annotation != null){
				Method method = null;
				if(!annotation.injectMethodName().equals("")){
					method = ReflectionUtils.findMethod(beanType, annotation.injectMethodName(),getMethodParams(beanType,annotation.injectMethodName()));
				}
				if(method == null){
					String filedName = field.getName();
					String guessName = "set"+filedName.substring(0,1).toUpperCase()+filedName.substring(1);
					method = ReflectionUtils.findMethod(beanType, guessName,getMethodParams(beanType, guessName));
				}
				if(method == null){
					continue;
				}
				keys.add(new ResourceNameKey(annotation,method));
			}
		}
		if(keys.size() > 0){
			for(ResourceNameKey key: keys){
				Object obj = beanFactory.getBean(key.getAnnotation().id());
				if(obj != null){
					//beanDefinition.addMetadataAttribute(new BeanMetadataAttribute(key.getMethod().getName(),obj));
					String methodName = key.getMethod().getName();
					if(methodName.startsWith("set")){
						methodName = methodName.substring(3);
					}
					beanDefinition.getPropertyValues().addPropertyValue(methodName, obj);
				}
			}
		}
	}
	
	private Class<?>[] getMethodParams(Class beanType, String injectMethodName) {
		Method[] methods = beanType.getMethods();
		for(Method method : methods){
				if(method.getName().equals(injectMethodName)){
					return method.getParameterTypes();
				}
		}
		return new Class[0];
	}

	private class ResourceNameKey{
		private ResourceName annotation;
		private Method method;
		
		public ResourceNameKey(ResourceName annotation,Method method){
			this.annotation = annotation;
			this.method = method;
		}

		public ResourceName getAnnotation() {
			return annotation;
		}

		public Method getMethod() {
			return method;
		}
		
	}

}
